package com.haoqizhe.kernel.security.config;

import cn.hutool.core.util.StrUtil;
import com.haoqizhe.kernel.jwt.utils.JwtTokenUtil;
import com.haoqizhe.kernel.commons.constants.HeaderConstants;
import com.haoqizhe.kernel.commons.exceptions.BusinessException;
import com.haoqizhe.kernel.commons.util.SpringContextUtil;
import com.haoqizhe.kernel.redis.common.util.RedisUtil;
import io.jsonwebtoken.ExpiredJwtException;
import lombok.extern.slf4j.Slf4j;
import org.springframework.core.env.Environment;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.web.authentication.www.BasicAuthenticationFilter;

import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.Collections;

/**
 * token拦截
 *
 * @author haoqizhe.li
 * @date 2018/10/16
 **/
@Slf4j
public class JwtAuthorizationFilter extends BasicAuthenticationFilter {

    private JwtTokenUtil jwtTokenUtil = null;

    public JwtAuthorizationFilter(AuthenticationManager authenticationManager) {
        super(authenticationManager);
        jwtTokenUtil = SpringContextUtil.getBean(JwtTokenUtil.class);
    }

    @Override
    protected void doFilterInternal(HttpServletRequest request,
                                    HttpServletResponse response,
                                    FilterChain chain) throws IOException, ServletException, BusinessException {
        log.debug("URI:" + request.getRequestURI());
        String tokenHeader = request.getHeader(HeaderConstants.X_TOKEN);


        // 如果请求头中没有Authorization信息则直接放行了
        if (tokenHeader == null || !tokenHeader.startsWith(HeaderConstants.TOKEN_PREFIX)) {
            chain.doFilter(request, response);
            return;
        }
        // 如果请求头中有token，则进行解析，并且设置认证信息
        try {
            SecurityContextHolder.getContext().setAuthentication(getAuthentication(tokenHeader));

            /**
             * 判断token失效时间 是否 小于 5分钟 刷新 token
             * 刷新token后 将token存进redis，以便后续旧的token请求接口时获取的token与刷新的一致
             * 刷新redis缓存的用户信息，applicationName+token为key
             */
            String token = tokenHeader.replace(HeaderConstants.TOKEN_PREFIX, "");
            if (jwtTokenUtil.isRefreshToken(token)) {
                RedisUtil redisUtil = SpringContextUtil.getBean(RedisUtil.class);
                Environment environment = SpringContextUtil.getBean(Environment.class);
                String applicationName = environment.getProperty("spring.application.name");
                String newToken = (String) redisUtil.get(token);

                boolean isRememberMe = jwtTokenUtil.getRememberMe(token);
                long expireTime = jwtTokenUtil.expire() + 100;
                if (isRememberMe) {
                    expireTime = jwtTokenUtil.rememberExpire() + 100;
                }
                if (StrUtil.isBlank(newToken)) {
                    newToken = jwtTokenUtil.refreshToken(token);
                    redisUtil.set(token, newToken, expireTime);
                }
                String redisUser = (String) redisUtil.get(applicationName + ":" + HeaderConstants.TOKEN_PREFIX + newToken);
                if (StrUtil.isBlank(redisUser)) {
                    redisUtil.set(applicationName + ":" + HeaderConstants.TOKEN_PREFIX + newToken,
                            redisUtil.get(applicationName + ":" + HeaderConstants.TOKEN_PREFIX + token), expireTime);
                }
                response.setHeader(HeaderConstants.X_TOKEN, HeaderConstants.TOKEN_PREFIX + newToken);
            }

        } catch (ExpiredJwtException e) {
            log.error(e.getMessage(), e);
        } catch (Exception e) {
            log.error(e.getMessage(), e);
        }
        super.doFilterInternal(request, response, chain);
    }

    /**
     * 这里从token中获取用户信息并新建一个token
     */
    private UsernamePasswordAuthenticationToken getAuthentication(String tokenHeader) {
        String token = tokenHeader.replace(HeaderConstants.TOKEN_PREFIX, "");
        String username = jwtTokenUtil.getUsername(token);
        String role = jwtTokenUtil.getClaimsFromToken(token).get("role").toString();
        if (username != null) {
            return new UsernamePasswordAuthenticationToken(username, null,
                    Collections.singleton(new SimpleGrantedAuthority(role))
            );
        }
        return null;
    }
}
